﻿// Copyright (c) Inossem. All rights reserved.
// Licensed under the LGPL-3.0 license. See LICENSE file in the project root for full license information.
using DWF.Studio.Share.Log;
using DWF.Studio.Share.Utils;
using NuGet.Commands;
using NuGet.Common;
using NuGet.Configuration;
using NuGet.Frameworks;
using NuGet.PackageManagement;
using NuGet.Packaging;
using NuGet.Packaging.Core;
using NuGet.ProjectManagement;
using NuGet.ProjectModel;
using NuGet.Protocol;
using NuGet.Protocol.Core.Types;
using NuGet.Resolver;
using NuGet.Versioning;
using System;
using System.Collections.Generic;
using System.IdentityModel.Protocols.WSTrust;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.ServiceModel.Description;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace DWF.Studio.NuGetManagement
{
    /// <summary>
    /// 包管理器
    /// </summary>
    public class PackageManager : IPackageManager
    {
        /// <summary>
        /// nuget服务器地址
        /// </summary>
        //public string _packageUri = null;
        /// <summary>
        /// 
        /// </summary>
        public List<PackageSourceUri> _packageUri = null;
        /// <summary>
        /// 日志
        /// </summary>
        private TextLogger _logger = null;
        /// <summary>
        /// 获取包的状态
        /// </summary>
        private GetPackageStatus _getPackageStatus;

        /// <summary>
        /// nuget用户名
        /// </summary>
        private string _nugetUsername = "";
        /// <summary>
        /// nuget密码
        /// </summary>
        private string _nugetPassword = "";
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="packageUri"></param>
        public PackageManager(List<PackageSourceUri> packageUri, GetPackageStatus getPackageStatus= GetPackageStatus.allPackageSource,string nugetUsername= "visitor", string nugetPassword="a123456")
        {
            _getPackageStatus = getPackageStatus;
            _packageUri = packageUri;
            _nugetUsername = nugetUsername;
            _nugetPassword = nugetPassword;
            _logger = new TextLogger() { IsOutput = false };
        }

        /// <summary>
        /// 检查包源地址是否存在包
        /// </summary>
        /// <param name="Id"></param>
        /// <param name="version"></param>
        /// <param name="packageUrl"></param>
        /// <returns></returns>
        private async Task<NuGetPackage> getPackageByIdAsync(string Id, string version,string packageUrl)
        {
            var identity = new PackageIdentity(Id, new NuGetVersion(version));

            List<Lazy<INuGetResourceProvider>> providers = new List<Lazy<INuGetResourceProvider>>();
            providers.AddRange(Repository.Provider.GetCoreV3());
            NuGetPackage package = null;

            var _packageUrl = packageUrl;
            var packageSource = new PackageSource(packageUrl, _nugetUsername);
            packageSource.Credentials = new PackageSourceCredential(_packageUrl, _nugetUsername, _nugetPassword, true);
            var sourceRepository = new SourceRepository(packageSource, providers);

            CancellationToken cancellationToken = new CancellationTokenSource(3000).Token;
            PackageMetadataResource packageMetadataResource = await sourceRepository.GetResourceAsync<PackageMetadataResource>(cancellationToken);
            var metadata = await packageMetadataResource.GetMetadataAsync(identity, new SourceCacheContext() { NoCache = true }, _logger, cancellationToken);

            if (metadata != null)
            {
                package = new NuGetPackage(metadata);
            }
            return package;
        }
        /// <summary>
        /// 通过id获取所有可用包源地址的nuget包
        /// </summary>
        /// <param name="Id"></param>
        /// <param name="version"></param>
        /// <returns></returns>
        public async Task<NuGetPackage> GetPackageByIdAsync(string Id, string version)
        {
            var identity = new PackageIdentity(Id, new NuGetVersion(version));

            List<Lazy<INuGetResourceProvider>> providers = new List<Lazy<INuGetResourceProvider>>();
            providers.AddRange(Repository.Provider.GetCoreV3());
            NuGetPackage package = null;
            List<string> packageSourceUri = await checkPackagesSource();
            
            for (int i = 0; i < packageSourceUri.Count; i++)
            {
                CancellationToken cancellationToken = new CancellationTokenSource(3000).Token;
                
                var packageSource = new PackageSource(packageSourceUri[i], _nugetUsername);
                packageSource.Credentials = new PackageSourceCredential(packageSourceUri[i], _nugetUsername, _nugetPassword, true);
                var sourceRepository = new SourceRepository(packageSource, providers);
                try
                {
                    PackageMetadataResource packageMetadataResource = await sourceRepository.GetResourceAsync<PackageMetadataResource>(cancellationToken);
                    var metadata = await packageMetadataResource.GetMetadataAsync(identity, new SourceCacheContext() { NoCache = true }, _logger, cancellationToken);
                    if (metadata != null)
                    {
                        package = new NuGetPackage(metadata);
                        break;
                    }
                }
                catch (Exception ex)
                {
                    LogHelper.Error(ex.ToString());
                    continue;
                }
            }
            return package;
        }
        /// <summary>
        /// 通过id获取本地已安装的地址的nuget包
        /// </summary>
        /// <param name="Id"></param>
        /// <param name="version"></param>
        /// <returns></returns>
        public async Task<NuGetPackageMetadata> GetLocalRoutePackageByIdAsync(string activitiePath)
        {
            if (!FileHelper.CheckFileExisting(activitiePath))
            {
                return null;
            }
            PackageArchiveReader packageArchiveReader = new PackageArchiveReader(activitiePath);
            var stream = await packageArchiveReader.GetNuspecAsync(CancellationToken.None);

            //NuspecReader nuspec = packageArchiveReader.NuspecReader;

            var manifestMetadata = Manifest.ReadFrom(stream, true).Metadata;
            NuGetPackageMetadata package = new NuGetPackageMetadata(manifestMetadata);

            return package;
        }
        /// <summary>
        /// 搜索nuget包
        /// </summary>
        /// <param name="seachString"></param>
        /// <param name="skip"></param>
        /// <param name="take"></param>
        /// <param name="includePrereleas"></param>
        /// <returns></returns>
        public async Task<IEnumerable<NuGetPackage>> SearchPackagesAsync(string seachString, int skip, int take, bool includePrereleas = false, bool isInstalled = false)
        {
            List<Lazy<INuGetResourceProvider>> providers = new List<Lazy<INuGetResourceProvider>>();
            providers.AddRange(Repository.Provider.GetCoreV3());
            List<NuGetPackage> packages = new List<NuGetPackage>();

            List<string> packageSourceUri = await checkPackagesSource();
            
            for (int i = 0; i < packageSourceUri.Count; i++)
            {
                CancellationToken cancellationToken = new CancellationTokenSource(3000).Token;
                
                var packageSource = new PackageSource(packageSourceUri[i], _nugetUsername);
                packageSource.Credentials = new PackageSourceCredential(packageSourceUri[i], _nugetUsername, _nugetPassword, true);
                var sourceRepository = new SourceRepository(packageSource, providers);

                try
                {
                    var packageMetadataResource = await sourceRepository.GetResourceAsync<PackageSearchResource>(cancellationToken);
                    var searchMetadatas = await packageMetadataResource.SearchAsync(seachString, new SearchFilter(includePrereleas), skip, take, _logger, cancellationToken);

                    foreach (var meta in searchMetadatas)
                    {
                        //判断包里是否存在
                        if (!isInstalled)
                        {
                            if (packages.Where(o => o.Id == meta.Identity.Id).FirstOrDefault() == null)
                            {
                                packages.Add(new NuGetPackage(meta));
                            }
                        }
                        else//已安装的包
                        {
                            if (packages.Where(o => o.Id == meta.Identity.Id).FirstOrDefault() == null)
                            {
                                packages.Add(new NuGetPackage(meta));
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogHelper.Error(ex.ToString());
                    continue;
                }
            }
            return packages;
        }

        /// <summary>
        /// 获取nuget的全部版本
        /// </summary>
        /// <param name="nugetPackage"></param>
        /// <returns></returns>
        public async Task<List<NuGetPackage>> GetPackagesWithAllVersionAsync(NuGetPackage nugetPackage)
        {
            List<Lazy<INuGetResourceProvider>> providers = new List<Lazy<INuGetResourceProvider>>();
            providers.AddRange(Repository.Provider.GetCoreV3());
            List<NuGetPackage> packages = new List<NuGetPackage>();
            List<string> packageSourceUri = await checkPackagesSource();
            
            for (int i = 0; i < packageSourceUri.Count; i++)
            {
                CancellationToken cancellationToken = new CancellationTokenSource(3000).Token;
               
                var packageSource = new PackageSource(packageSourceUri[i], _nugetUsername);
                packageSource.Credentials = new PackageSourceCredential(packageSourceUri[i], _nugetUsername, _nugetPassword, true);
                var sourceRepository = new SourceRepository(packageSource, providers);

                try
                {
                    PackageMetadataResource packageMetadataResource = await sourceRepository.GetResourceAsync<PackageMetadataResource>(cancellationToken);
                    IEnumerable<IPackageSearchMetadata> searchMetadatas = await packageMetadataResource.GetMetadataAsync(nugetPackage.Id, true, true, new SourceCacheContext() { NoCache = true }, _logger, cancellationToken);

                    foreach (var meta in searchMetadatas)
                    {
                        //判断包里是否存在
                        if (packages.Where(o => o.Id == meta.Identity.Id && o.Version == meta.Identity.Version.ToFullString()).FirstOrDefault() == null)
                        {
                            packages.Add(new NuGetPackage(meta));
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogHelper.Error(ex.ToString());
                    continue;
                }
            }
            if(packages.Count>0)
                packages.OrderByDescending(item => item.Identity.Version);
            return packages;
        }

        /// <summary>
        /// 获取nuget的全部版本
        /// </summary>
        /// <param name="nugetPackage"></param>
        /// <returns></returns>
        public async Task<List<NuGetPackage>> GetPackagesWithAllVersionAsync(string NuGetPackageId,bool isDelay=false)
        {
            List<Lazy<INuGetResourceProvider>> providers = new List<Lazy<INuGetResourceProvider>>();
            providers.AddRange(Repository.Provider.GetCoreV3());
            List<NuGetPackage> packages = new List<NuGetPackage>();

            List<string> packageSourceUri = await checkPackagesSource();

            for (int i = 0; i < packageSourceUri.Count; i++)
            {
                CancellationToken cancellationToken = new CancellationTokenSource(3000).Token;
                if (isDelay) await Task.Delay(200);//延迟解决连续请求被服务器屏蔽的问题

                var packageSource = new PackageSource(packageSourceUri[i], _nugetUsername);
                packageSource.Credentials = new PackageSourceCredential(packageSourceUri[i], _nugetUsername, _nugetPassword, true);
                var sourceRepository = new SourceRepository(packageSource, providers);
                try
                {
                    PackageMetadataResource packageMetadataResource = await sourceRepository.GetResourceAsync<PackageMetadataResource>(cancellationToken);
                    IEnumerable<IPackageSearchMetadata> searchMetadatas = await packageMetadataResource.GetMetadataAsync(NuGetPackageId, true, true, new SourceCacheContext() { NoCache = true }, _logger, cancellationToken);

                    foreach (var meta in searchMetadatas)
                    {
                        if (packages.Where(o => o.Id == meta.Identity.Id && o.Version == meta.Identity.Version.ToFullString()).FirstOrDefault() == null)
                        {
                            packages.Add(new NuGetPackage(meta));
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogHelper.Error(ex.ToString());
                    continue;
                }
            }
            if(packages.Count>0)
                packages.OrderByDescending(item => item.Identity.Version);

            return packages;
        }
        /// <summary>
        /// 安装nuget包
        /// </summary>
        /// <param name="nugetPackage"></param>
        /// <param name="installPath"></param>
        /// <returns></returns>
        public async Task<IEnumerable<NuGetPackageDependency>> InstallPackageAsync(NuGetPackage nugetPackage, string installPath)
        {
            List<Lazy<INuGetResourceProvider>> providers = new List<Lazy<INuGetResourceProvider>>();
            providers.AddRange(Repository.Provider.GetCoreV3());

            var settings = Settings.LoadDefaultSettings(installPath, null, new MachineWideSettings());
            var packageSourceProvider = new PackageSourceProvider(settings);
            var sourceRepositoryProvider = new SourceRepositoryProvider(packageSourceProvider, providers);

            var project = new FolderNuGetProject(installPath);
            var packageManager = new NuGetPackageManager(sourceRepositoryProvider, settings, installPath)
            {
                PackagesFolderNuGetProject = project
            };

            var allowPrereleaseVersions = true;
            var allowUnlisted = false;
            INuGetProjectContext projectContext = new ProjectContext();
            ResolutionContext resolutionContext = new ResolutionContext(DependencyBehavior.Lowest, allowPrereleaseVersions, allowUnlisted, VersionConstraints.None);
            List<NuGetPackageDependency> dependencies = new List<NuGetPackageDependency>();

            List<string> packageSourceUri = await checkPackagesSource();

            for (int i = 0; i < packageSourceUri.Count; i++)
            {
                CancellationToken cancellationToken = new CancellationTokenSource(3000).Token;
                
                var packageSource = new PackageSource(packageSourceUri[i], _nugetUsername);
                packageSource.Credentials = new PackageSourceCredential(packageSourceUri[i], _nugetUsername, _nugetPassword, true);
                SourceRepository sourceRepository = new SourceRepository(packageSource, providers);
                try
                {
                    var isNugetPackageNull = await getPackageByIdAsync(nugetPackage.Id, nugetPackage.Version, packageSourceUri[i]);//获取要安装的活动包
                    if (isNugetPackageNull != null)
                    {
                        await packageManager.InstallPackageAsync(project, nugetPackage.Identity, resolutionContext, projectContext, sourceRepository, new List<SourceRepository>(), CancellationToken.None);

                        var dependencySets = nugetPackage.PackageSearchMetadata.DependencySets;
                        if (dependencySets.Count() > 0)
                        {
                            foreach (var dependencyGroup in dependencySets)
                            {
                                foreach (var dependency in dependencyGroup.Packages)
                                {
                                    //查询是否存在
                                    if (dependencies.Where(o => o.Id == dependency.Id && o.Version == dependency.VersionRange.OriginalString).FirstOrDefault() == null)
                                    {
                                        NuGetPackageDependency packageDependency = new NuGetPackageDependency()
                                        {
                                            Id = dependency.Id,
                                            Version = dependency.VersionRange.OriginalString,
                                            Dependency = dependency
                                        };
                                        dependencies.Add(packageDependency);
                                    }
                                }
                            }
                        }
                        break;
                    }
                }
                catch (Exception ex)
                {
                    LogHelper.Error(ex.ToString());
                    continue;
                }
            }
            return dependencies;
        }
        /// <summary>
        /// 检查包源路径
        /// </summary>
        /// <returns></returns>
        private async Task<List<string>> checkPackagesSource()
        {
            List<string> packageSourceUri = new List<string>();
            switch (_getPackageStatus)
            {
                case GetPackageStatus.localPackagesSource:
                    packageSourceUri = _packageUri.Where(o => o.Name == "Local" && o.IsDefault == true && o.Apply == true).Select(o => o.PackageUrl).ToList(); break;
                case GetPackageStatus.officialPackageSource:
                    packageSourceUri = _packageUri.Where(o => o.Name == "Official" && o.IsDefault == true && o.Apply == true).Select(o => o.PackageUrl).ToList(); break;
                case GetPackageStatus.customPackageSource:
                    packageSourceUri = _packageUri.Where(o => o.IsDefault == false && o.Apply == true).Select(o => o.PackageUrl).ToList(); break;
                case GetPackageStatus.allPackageSource:
                    packageSourceUri = _packageUri.Where(o => o.Apply == true).Select(o => o.PackageUrl).ToList(); break;
            }
            for (int i = 0; i < packageSourceUri.Count; i++)
            {
                if (packageSourceUri[i].Length <= 5)
                {
                    packageSourceUri.Remove(packageSourceUri[i]);
                    continue;
                }
                    
                if (packageSourceUri[i].Substring(0, 4) == "http")
                {
                    if (!await this.existFromUrl(packageSourceUri[i]))
                    {
                        packageSourceUri.Remove(packageSourceUri[i]);
                        continue;
                    }
                }
                else
                {
                    //检测路径不存在
                    if (!FileHelper.CheckFolderExisting(packageSourceUri[i]))
                    {
                        packageSourceUri.Remove(packageSourceUri[i]);
                        continue;
                    }
                }
            }
            
            return packageSourceUri;
        }
        /// <summary>
        /// 测试连接是否可用  为了解决 服务器无响应问题 ，但是会连接两次
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        private async Task<bool> existFromUrl(String url)
        {
            await Task.CompletedTask;
            return true;
        }
    }
    /// <summary>
    /// 0-路径为空,1-存在文件,2-路径不为空,但文件不存在
    /// </summary>
    public enum FileExsitStatus
    {
        NoPath = 0,
        FileExsit = 1,
        NoFile = 2
    }
}
